<?php

/*************************************************************************/
/**
 * 密码加密
 * 该方法加密不能反推明文,同一字符每次执行哈希值都不一样
 * PASSWORD_DEFAULT---使用bcrypt算法.注意：该常量会随着 PHP 加入更新更高强度的算法而改变,未来可能会有变化。 因此，数据库里储存结果的列可超过60个字符（最好是255个字符）.
 * @param string $password
 * @return bool|string
 */
function encrypt_password($password='')
{
    /**
     * 使用 password_hash()你完全可以不提供盐值(salt)和 消耗值 (cost)，你可以将后者理解为一种性能的消耗值，cost越大，加密算法越复杂，消耗的内存也就越大。当然，如果你需要指定对应的盐值和消耗值，你可以这样写
     * $options = [
     'salt' => custom_function_for_salt(), //自定义函数来获得盐值
     'cost' => 12 // the default cost is 10
     ];
    $hash = password_hash($password, PASSWORD_DEFAULT, $options);
     */
  return password_hash($password,PASSWORD_DEFAULT);
}

/**
 * 密码解密（不能反向解密）
 * 验证密码是否与散列匹配
 * @param string $password
 * @param string $hash
 * @return bool
 */
   function decrypt_password($password = '', $hash = '')
{
    if (password_verify($password, $hash)) {
        return true;
    }
    return false;
}
/*************************************************************************/

/**
 * 对字符串(标签特殊字符)多个替换
 * @param $string  原字符串
 * @param $replacer 批量替换的数据以数组键值对形式
 * @return mixed
 */
   function stringParser($string,$replacer){
    /**
     * str_replace(mixed $search ,mixed $replace,mixed $subject)
     *  search 和 replace 为数组$subject的值将会被映射替换依次处理, replace 的值的个数少于 search 的多余的会使用空字符串来替换.
     *  search 是一个数组而 replace 是一个字符串，那么 search 中每个元素的替换将始终使用这个字符串
     * $subject 如果是数组，替换操作将遍历整个 subject，返回值也将是一个数组
     */
    $result = str_replace(array_keys($replacer), array_values($replacer),$string);
    return $result;
}


/*************************************************************************/
/* 
 Utf-8、gb2312都支持的汉字截取函数 
 cut_str(字符串, 截取长度, 开始长度, 编码); 
 编码默认为 utf-8 
 开始长度默认为 0 
*/ 
function cutStr($string, $sublen, $start = 0, $code = 'UTF-8'){ 
    if($code == 'UTF-8'){ 
        $pa = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|\xe0[\xa0-\xbf][\x80-\xbf]|[\xe1-\xef][\x80-\xbf][\x80-\xbf]|\xf0[\x90-\xbf][\x80-\xbf][\x80-\xbf]|[\xf1-\xf7][\x80-\xbf][\x80-\xbf][\x80-\xbf]/"; 
        preg_match_all($pa, $string, $t_string); 
        //将匹配到的utf8字符 
        if(count($t_string[0]) - $start > $sublen) return join('', array_slice($t_string[0], $start, $sublen))."..."; 
        return join('', array_slice($t_string[0], $start, $sublen)); 
    }else{ 
        $start = $start*2; 
        $sublen = $sublen*2; 
        $strlen = strlen($string); 
        $tmpstr = ''; 
 
        for($i=0; $i<$strlen; $i++){ 
            if($i>=$start && $i<($start+$sublen)){ 
                if(ord(substr($string, $i, 1))>129){ 
                    $tmpstr.= substr($string, $i, 2); 
                }else{ 
                    $tmpstr.= substr($string, $i, 1); 
                } 
            } 
            if(ord(substr($string, $i, 1))>129) $i++; 
        } 
        if(strlen($tmpstr)<$strlen ) $tmpstr.= "..."; 
        return $tmpstr; 
    } 
} 



/*
*此方法能够剥离html标签xml还有注释等只保留字符串
* //php内置的字符串长度函数无法正确处理中文字符串， 在gb2312中文编码，strlen的到的值是汉字个数的2倍，对于utf-8这是3倍差异，mb_substr刚好解决这个问题统一占一个。注意m*b_strlen并不是php核心函数，使用前确保在php.ini中加载了php_mbstring.dll.。同样有类似的mb_substr方法也是一样的并不是php的核心文件,否则就必须判断通过不同方法解决
*这种方法推荐，有mabstr使用mbstr没有就正则判断
*/
function cnsubstr($str, $length, $start=0, $charset="utf-8", $suffix=true)
{
       $str = strip_tags($str); //去除标签
         
       if(function_exists("mb_substr"))
       {
               if(mb_strlen($str, $charset) <= $length) return $str;
               $slice = mb_substr($str, $start, $length, $charset);
       }
       else
       {
               $re['utf-8']   = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
               $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
               $re['gbk']          = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
               $re['big5']          = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
               preg_match_all($re[$charset], $str, $match);//通过正则进行匹配一个一个字匹配并进行拆分成从0开始二维数组要取得$match[0]
               if(count($match[0]) <= $length) return $str; //在判断数组中字的个数
               $slice = join("",array_slice($match[0], $start, $length));//array_slice方法从什么开始取一直取到数组末端，组成一个数组再通过这个join把数组组合成一个字符串
       }
       if($suffix) return $slice."…";
       return $slice;
}


/**
 * substr_replace函数有些缺陷，中文替代会出现乱码，使用mb_substr函数截取函数再连接字符串解决
 * $str = "中华人民共和国";
 * 支持自定义字符串首位的个数 中华**共和国
 * 支持首位 中华****
 * 支持后缀的类别 如****
 */

function strmidreplace($str,$before,$pre,$after)
{

    $len = mb_strlen($str,'utf-8');

    if(!$pre) echo "后缀不能未空";

    //表示首位是空的,默认首位只占一个字符
    if(empty($before)&&empty($after)){
        $str1 = mb_substr($str,0,1,'utf-8');
        $str2 = mb_substr($str,$len-1,1,'utf-8');
        $str=$str1.$pre.$str2;
    }
    elseif(!empty($before)&&!empty($after)){
        //首位不是空的 判断首位长度是否超过字符总长度,超过默认占一位
        if($len>($before+$after)){
            $str1 = mb_substr($str,0,$before,'utf-8');
            $str2 = mb_substr($str,-$after);
        }else{
            $str1 = mb_substr($str,0,1,'utf-8');
            $str2 = mb_substr($str,-1);
        }

        $str=$str1.$pre.$str2;
    }elseif (!empty($before)){
        //只有首位, 判断首位长度时候超过总长度超过则默认占一位
        if($len<=$before)
            $str = mb_substr($str,0,1,'utf-8').$pre;
        else $str = mb_substr($str,0,$before,'utf-8').$pre;
    }

    echo $str;
}






/*************************************************************************/

/**
 *
 *  RSA签名对数据进行签名加密
 * @param $data    要签名的数据;
 * @param $file_pem 私钥文件的路径
 * @return string   签名结果
 * 总结：公钥和私钥是成对的，它们互相解密。公钥加密，私钥解密,私钥数字签名，公钥验证
 * 私钥加密->公钥解密 私钥加密的内容通过公钥可用解密出来
 * openssl_private_encrypt($data, $encrypted, $priKey);base64_encode($encrypted);
 * openssl_public_decrypt(base64_decode($encrypted),$decrypted,$pubKey);
 * 公钥加密->私钥解密 公钥加密的内容通过私钥可用解密出来
 *  openssl_public_encrypt($data, $encrypted, $pubKey);base64_encode($encrypted);
 * openssl_private_decrypt(base64_decode($encrypted),$decrypted,$priKey)
 */
function rsasign($data, $file_prem)
{
    //获取私钥文件内容
    $priKey=file_get_contents($file_prem);
    //转换为openssl密钥，必须是没有经过pkcs8转换的私钥
    $priv_key_id=openssl_get_privatekey($priKey);
    //调用签名函数生成签名$sign .$sign签名结果引用变量 .$priv_key_id为签名所使用的密匙
    openssl_sign($data,$sign,$priv_key_id);
    //从内存中释放私钥释放资源
    openssl_free_key($priv_key_id);
    //使用base64对签名进行编码加密
    $sign = base64_encode($sign);
    return $sign

}

/**
 * 使用RSA,来验证签名的数据
 * @param $data  签名之前的数据
 * @param $sign  签名加密后的数据
 * @param $file_ppem  公钥文件的路径
 * @return bool  验证结果
 * 总结：公钥和私钥是成对的，它们互相解密。公钥加密，私钥解密,私钥数字签名，公钥验证
 */
 function rsaVerify($data, $sign, $file_ppem) {
    //获取公钥文件内容
    $pubKey = file_get_contents($file_ppem);
    //转换为openssl密钥，并获取私钥
    $res = openssl_get_publickey($pubKey);
    //调用openssl内置方法验签函数来验证签名，返回bool值。
    $result = (bool)openssl_verify($data, base64_decode($sign), $res);
    //从内存中释放私钥释放资源
    openssl_free_key($res);
    return $result;
}
/*************************************************************************/

/**
 *  将XML字符串转换成SimpleXML对象
 *
 * @param $StringXml  xml字符串
 * @return SimpleXMLElement   simplexml对象
 */
function StringXml2Obj($StringXml)
{
    //simplexml_load_string($string,$class_name = "SimpleXMLElement",$option)
    //$string:xml字符串数据.
    //$class_name:需要转变的对象,默认SimpleXMLElement可以不用写
    //$option:设置LIBXML_NOCDATA.如果无法读取CDATA标记内部数值,使用此常量能够解析cdata标记,去除CDATA标记，获得标记段的内容
    $SimpleXmlElement = simplexml_load_string($StringXml, "SimpleXMLElement", LIBXML_NOCDATA);
    return $SimpleXmlElement;
}

/**
 *
 * 将simplexml对象转为数组
 *
 * <?xml version="1.0" encoding="UTF-8"?>
 * <note>                         object(SimplexMLElement)
 *    <heading>Reminder</heading>       public 'heading' => string 'Reminder'
 *    <body>                       object(SimpleXMLElement)
 *        <head>头</head>             public 'head' => string '头' (length=3)
 *        <text>内容</text>           public 'text是' => string '内容' (length=6)
 *    </body>
   </note>
 * [xml中含内容的标签都是一组对象]
 * @param $obj simplexml对象
 * @return mixed
 * 总结：xml标签如果一层内又一层需要循环遍历把内部没有转化的对转成数组
 * 循环到当前键值是obj -->obj下面时候还有obj
 * 循环到键值是数组array ---->该数组下时候有值是obj
 */
function simplexml2array($obj)
{
    if (is_object($obj)) {
        //设置变量类型为数组类型  obj->array
        settype($obj, "array");
    }
    foreach ($obj as $k => $v) {
        //判断数组和对象内时候有内容,如果数组中还有内容进行迭代遍历,把含对象的都转话成数组
        if (is_array($v) || is_object($v)) {
            $obj[$k] = count($v) ? simplexml2array($v) : '';
        }
    }
    return $obj;
}

/*************************************************************************/
/**
 * 将字符串转换成16进制
 * @param $str
 * @return mixed
 */
function str2hex($str)
{
    $arr=str_split($str);//分割字符(不懂请看函数笔记)
    foreach ($arr as $v) {
    $ascll=ord($v);    //获取每个字符的ascall码(十进制)
    $hex .=dechex($ascll);  //将每个字符的16进制拼起来。组合成一个完整的进制数
    }
    return $hex;
}

/**
 * 将16进制转换成字符
 * @param $hex
 * @return string
 */
function hex2str($hex)
{
   $arr=str_split($hex,2);//将拼接好的多个16进制数两个一组,分割
    foreach ($arr as $v) {
        $ascll=hexdec($v);  //将16进制再转会十进制ascll码值
        $str .=chr($ascll);//将ascll码转换成字节(字符),三个字节拼成一个汉字(不懂看函数笔记)
    }
    return $str;
}

/**
 * 不使用str_split,直接操作字符
 * strlen($str)
 *$str="翟玉进"
 */
function str2ascll($str)
{
    $res=[];
    $length=strlen($str);//获取字符串长度,字母占一一个字节,中文占三个字节。举例子：翟玉进 就是9为
    for ($i = 0, $i < $lenght; $i++) {
        $res[]= ord($str[$i]).".".ord($str[++$i]).".".ord($str[++$i]) //如果是utf8,中文前三个长度代表一个汉字，gbk 2个代码一个汉字注意
    }
   //$res数组中每个元素三个字符Ascll码值拼接,以便于拆解拼接汉字
   /******将数据拆解开始拼接汉字********/
    foreach ($res as $v) {
        $desc=explode(".",$v);
        echo chr($desc[0]).chr($desc[1]).chr($desc[2])
    }
}



/**
 * 字符集转换,支持数组
 *  如果是字符串：
 *  支持mb_convert_encoding可以直接使用该函数来转化
 *  支持iconv可以直接使用该函数来转化
 *  如果是数组：
 * 可以使用循环迭代来转化
 * @param $fContents
 * @param string $from
 * @param string $to
 * @return array|string
 */
function auto_charset($fContents, $from='gbk', $to='utf-8') {
    $from = strtoupper($from) == 'UTF8' ? 'utf-8' : $from;
    $to = strtoupper($to) == 'UTF8' ? 'utf-8' : $to;
    if ( ($to=='utf-8'&&is_utf8($fContents)) || strtoupper($from) === strtoupper($to) || empty($fContents) || (is_scalar($fContents) && !is_string($fContents))) {
        //如果字符本身是utf8,$to还是utf8,编码相同不需要转换,from和to都是utf8,字符是空的,不是字符串的不转换
        //标量(integer、float、string、boolean是标量，array、object、resource不是标量)
        return $fContents;
    }
    if (is_string($fContents)) {
        if (function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($fContents, $to, $from);
        } elseif (function_exists('iconv')) {
            return iconv($from, $to, $fContents);
        } else {
            return $fContents;
        }
    } elseif (is_array($fContents)) {
        foreach ($fContents as $key => $val) {
            //将键值和简明都要转化,如果没有键值编码有问题删除该键值
            $_key = auto_charset($key, $from, $to);
            $fContents[$_key] = auto_charset($val, $from, $to);
            if ($key != $_key)
                unset($fContents[$key]);
        }
        return $fContents;
    }
    else {
        return $fContents;
    }
}

/**
 *判断字符串是否为utf8编码，英文和半角字符返回true
 *该函数能够准确判断是否正则返回boolean ,精确度和mb_detect_encoding几乎一样
 */
function is_utf8($string) {
    return preg_match('%^(?:
         [\x09\x0A\x0D\x20-\x7E]            # ASCII
       | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
       |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
       | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
       |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
       |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
       | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
       |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
   )*$%xs', $string);
}
/*************************************************************************/